//
//  GameMatrix.cpp
//  Chart
//
//  Created by CaoJun on 15/3/30.
//
//

#include "GameMatrix.h"
#include "GameMatrixSQLite.h"

using namespace ytx;
using namespace std;


GameMatrix::GameMatrix(IndexConfig indexConfig):IndexData(indexConfig, 0), requesLock(false)
{
    indexName = "GameMatrix";
    type = QuotationType::GameMatrix;
}

void GameMatrix::callback(string tag, string data)
{
    YTXJson::Reader reader;
    YTXJson::Value value;
    
    try {
        if (reader.parse(data, value)) {
            requesLock = false;
        }
        
        if (this->kLineData == nil) {
            return;
        }
        
        if(tag == "GameMatrixData"+this->kLineData->id)
        {
            responseGameMatrixData(value["datas"], value["info"]);
        }
        else if(tag == "GameMatrixDataFront"+this->kLineData->id)
        {
            responseGameMatrixDataFront(value["datas"], value["info"]);
        }
        else if(tag == "GameMatrixDataBack"+this->kLineData->id)
        {
            responseGameMatrixDataBack(value["datas"], value["info"]);
        }
    } catch (void * __attribute__((annotate("oclint:suppress[unused local variable]"))) ex) {
        NSLog(@"%@", ex);
    }
}

void GameMatrix::onFail(string tag)
{
    requesLock = false;
}

void GameMatrix::realCalculate(KLineData * data)
{
    indexData.clear();
    lineData.clear();
    
    if ( gameMatrixData.size() == 0 || !data->hasDataWithoutClose()) {
        return;
    }
    
    //先设置为灰色
    vector<double> type;
    
    //lineData为了参与最高最低的计算
    vector<double> lineDataBound1;
    vector<double> lineDataBound2;
    
    int gmIndexFront = getGameMatrixIndexInKLinedataFront( data );
    int gmIndexBack = getGameMatrixIndexInKLinedataBack( data );

    int gmIndex = gmIndexFront < 0 ? 0 : gmIndexFront;
    
    int index = 0;
    
    GameMatrixItem gmItem;
    //怎么做为了不提高算法复杂度，否则为n * n, 300 * 300 = 90,000
    for (vector<KLineItem>::iterator kLineIt = data->kLineData.begin(); kLineIt != data->kLineData.end(); ++kLineIt) {
        KLineItem k = (*kLineIt);
        index = (int)(kLineIt - data->kLineData.begin());
        
        if (gmIndex == index && gmIndex <= gmIndexBack){
            gmItem = gameMatrixData[MIN(gmIndex - gmIndexFront, gameMatrixData.size() - 1)];
            type.push_back(gmItem.type);
            lineDataBound1.push_back(gmItem.bound1);
            lineDataBound2.push_back(gmItem.bound2);
            gmIndex++;
        }
        else {
            type.push_back(0);
            lineDataBound1.push_back(k.open);
            lineDataBound2.push_back(k.close);
        }
    }
    
    indexData.push_back(type);
    lineData.push_back(lineDataBound1);
    lineData.push_back(lineDataBound2);
}

int GameMatrix::getGameMatrixIndexInKLinedataFront(KLineData * data)
{

    int index = getGameMatrixIndexInKLinedata(gameMatrixData.front().tradedate, data);
    // KLineData:     ==========
    // GameMatrix:   ===========
    if (index == -1 && data->getDataLength() < getDataLength()) {
        index = data->getDataLength() - getDataLength();
    }
    
    return index;
}

int GameMatrix::getGameMatrixIndexInKLinedataBack(KLineData * data)
{
    
    int index = getGameMatrixIndexInKLinedata(gameMatrixData.back().tradedate, data);
    // KLineData:    ==========
    // GameMatrix:   ============
    if (index == -1 && data->getDataLength() < getDataLength()) {
        index = data->getDataLength() - 1;
    }
    
    return index;
}

int GameMatrix::getGameMatrixIndexInKLinedata(YTXTime time, KLineData * data)
{
    if (time >= data->kLineData.back().dateTime) {
        return data->getDataLength() - 1;
    }
    
    for (vector<KLineItem>::iterator kLineIt = data->kLineData.begin(); kLineIt != data->kLineData.end() - 1; ++kLineIt) {
        KLineItem k = (*kLineIt);
        
        if (k.dateTime == time)
        {
            return (int)(kLineIt - data->kLineData.begin());
        }
        
    }
    return -1;
}


void GameMatrix::calculate(Quotation * data)
{
    this->kLineData = (KLineData *)data;
    getGameMatrixData();
}

void GameMatrix::updateLastest(Quotation * data)
{
    calculate(data);
}

bool GameMatrix::hasGameMatrixData()
{
    return gameMatrixData.size() > 0;
}

//调用时需要判断有没有QK
//getK已经有数据 或者 response 监听分时变化时也要判断
void GameMatrix::getGameMatrixData()
{
    GameMatrixSQLite gameMatrixSQLite;
    
    gameMatrixSQLite.createKLineDataTableIfNotExit(GameMatrixSQLite::getTableName(kLineData));
    
    if (!kLineData->hasDataWithoutClose()) {
        return;
    }
    
    if (!hasGameMatrixData()) {
        //+1是算出来那条的长度
        gameMatrixData = getGameMatrixDataFromDB(YTXTime(2200,1,1,0,0,0), kLineData->getDataLengthWithoutClose()+1);
    }
//    NSLog(@"klength:%d thislength:%d", kLineData->getDataLength(), getDataLength());
    if (hasGameMatrixData()) {
        bool cacheInvalidation = kLineData->isCacheInvalidation(gameMatrixData.back().tradedate, YTXTime().GMT8());
        
        if (!cacheInvalidation) {
            cleanData();
            gameMatrixSQLite.clearTable(GameMatrixSQLite::getTableName(this));
            requestGameMatrixData();
            return;
        }
        
//        NSString * kf = [NSString stringWithUTF8String: this->kLineData->kLineData.front().dateTime.toStringMMddHHmmss().c_str()];
//        NSString * gmf = [NSString stringWithUTF8String: gameMatrixData.front().tradedate.toStringMMddHHmmss().c_str()];
//        
//        NSString * kb = [NSString stringWithUTF8String: this->kLineData->kLineData.back().dateTime.toStringMMddHHmmss().c_str()];
//        NSString * gmb = [NSString stringWithUTF8String: gameMatrixData.back().tradedate.toStringMMddHHmmss().c_str()];
//        
//        NSLog(@"kf:%@ gmf:%@",kf, gmf);
//        NSLog(@"kb:%@ gmb:%@",kb, gmb);

        
        if (gameMatrixData.back().tradedate < kLineData->kLineData.back().dateTime) {
            //取未来
            getGameMatrixDataBack();
        }
        else if (gameMatrixData.front().tradedate > kLineData->kLineData.front().dateTime) {
            //取历史
            getGameMatrixDataFront();
        }

        realCalculate(this->kLineData);
    }
    else {
        requestGameMatrixData();
    }
    
}

void GameMatrix::getGameMatrixDataFront()
{
    vector<GameMatrixItem> gmList = getGameMatrixDataFromDB(gameMatrixData.front().tradedate, this->kLineData->getLimit());
    
    if(gmList.size()){
        gameMatrixData.insert(gameMatrixData.begin(), gmList.begin(), gmList.end());
    }
    
    if (gameMatrixData.front().tradedate > kLineData->kLineData.front().dateTime) {
        requestGameMatrixDataFront(gameMatrixData.front().tradedate.toStringYYYYMMddHHmmss());
    }

}

void GameMatrix::getGameMatrixDataBack()
{
    requestGameMatrixDataBack(gameMatrixData.back().tradedate.toStringYYYYMMddHHmmss());
}

void GameMatrix::requestGameMatrixData()
{
    if (requesLock) {
        return;
    }
    int type = LineType::getRequestParamOfQuotationType(this->kLineData->quotationType);
    
    Config * config = Config::getInstance();
    
    map<string, string> data;
    
    data.insert(make_pair("sid", ConvertToString(this->kLineData->id)));
    
    data.insert(make_pair("quotationType", ConvertToString(type)));
    
    data.insert(make_pair("limit", ConvertToString(this->kLineData->getDataLength())));
    
    string routing = config->communicate.GameMatrixRouting;
    DataOption dataOption = DataOption(data, routing);
    requesLock = true;
    requestData("GameMatrixData"+this->kLineData->id, dataOption, "text/plain");
}

void GameMatrix::requestGameMatrixDataBack(string date)
{
    if (requesLock) {
        return;
    }
    int type = LineType::getRequestParamOfQuotationType(this->kLineData->quotationType);
    
    Config * config = Config::getInstance();
    
    map<string, string> data;
    
    data.insert(make_pair("sid", ConvertToString(this->kLineData->id)));
    
    data.insert(make_pair("quotationType", ConvertToString(type)));
    
    data.insert(make_pair("tradedate", date));
    
    data.insert(make_pair("limit", ConvertToString(this->kLineData->getLimit())));
    
    data.insert(make_pair("sort", "gt"));
    
    string routing = config->communicate.GameMatrixRouting;
    DataOption dataOption = DataOption(data, routing);
    requesLock = true;
    requestData("GameMatrixDataBack"+this->kLineData->id, dataOption, "text/plain");
}


void GameMatrix::requestGameMatrixDataFront(string date)
{
    //保证K线的数据领先
    if (requesLock) {
        return;
    }
    int type = LineType::getRequestParamOfQuotationType(this->kLineData->quotationType);
    
    Config * config = Config::getInstance();
    
    map<string, string> data;
    
    data.insert(make_pair("sid", ConvertToString(this->kLineData->id)));
    
    data.insert(make_pair("quotationType", ConvertToString(type)));
    
    data.insert(make_pair("tradedate", date));
    
    data.insert(make_pair("limit", ConvertToString(this->kLineData->getLimit())));
    
    data.insert(make_pair("sort", "lt"));
    
    string routing = config->communicate.GameMatrixRouting;
    DataOption dataOption = DataOption(data, routing);
    requesLock = true;
    requestData("GameMatrixDataFront"+this->kLineData->id, dataOption, "text/plain");
}

vector<GameMatrixItem> GameMatrix::parseGameMatrixData(YTXJson::Value data, YTXJson::Value info)
{
    vector<GameMatrixItem> ret;
    YTXJson::Value item;
    double bound1, bound2;
    YTXTimeDate tradedate;
    GameMatrixType::GameMatrixType type;
    for (YTXJson::Value::iterator it = data.begin(); it != data.end(); ++it) {
        item = *it;
        bound1 = item["bound1"].asDouble();
        bound2 = item["bound2"].asDouble();
        
        type = ( GameMatrixType::GameMatrixType)item["longshort"].asInt();
        
        tradedate = YTXTime(item["tradedate"].asString());
        
        GameMatrixItem gm = {
            tradedate,
            type,
            bound1,
            bound2
        };
        
        ret.push_back(gm);
    }
    
    // info?
    return ret;
}

void GameMatrix::responseGameMatrixData(YTXJson::Value data, YTXJson::Value info)
{
    vector<GameMatrixItem> ret = parseGameMatrixData(data, info);
    initGameMatrixData(ret);
}

void GameMatrix::responseGameMatrixDataFront(YTXJson::Value data, YTXJson::Value info)
{
    vector<GameMatrixItem> ret = parseGameMatrixData(data, info);
    insertFrontGameMatrixData(ret);
}

void GameMatrix::responseGameMatrixDataBack(YTXJson::Value data, YTXJson::Value info)
{
    vector<GameMatrixItem> ret = parseGameMatrixData(data, info);
    pushGameMatrixData(ret);
}

void GameMatrix::initGameMatrixData(vector<GameMatrixItem> gmList)
{
    gameMatrixData = gmList;
    saveGameMatrixDataToDB(gameMatrixData);
    this->kLineData->fire("load", NULL);
}

void GameMatrix::pushGameMatrixData(vector<GameMatrixItem> gmList)
{
    if (gmList.size() == 0 || gmList.front().tradedate <= gameMatrixData.back().tradedate) {
        return;
    }
    gameMatrixData.insert(gameMatrixData.end(), gmList.begin(), gmList.end());
    saveGameMatrixDataToDB(gmList);
    this->kLineData->fire("load", NULL);
}


void GameMatrix::insertFrontGameMatrixData(vector<GameMatrixItem> gmList)
{
    if (gmList.size() == 0 || gmList.back().tradedate >= gameMatrixData.front().tradedate) {
        return;
    }
    gameMatrixData.insert(gameMatrixData.begin(), gmList.begin(), gmList.end());
    saveGameMatrixDataToDB(gmList);
    this->kLineData->fire("load", NULL);
}

//bool GameMatrix::isInLatestNewK(YTXTime tradedate1, YTXTime tradedate2){
//    if (LineType::isMinuteLine(quotationType)){
//        return (tradedate1 + minutes( LineType::getRequestParamOfQuotationType(quotationType) ) ) >= tradedate2;
//    }
//    else if (quotationType == LineType::L1d) {
//        return (tradedate1 + days(1) ) >= tradedate2;
//    }
//    else if (quotationType == LineType::L1w) {
//        YTXTimeDuration dateDiff = tradedate2.date() - tradedate1.date();
//        return  LineType::getRequestParamOfQuotationType(quotationType) >= dateDiff.getDiffDay();
//    }
//    else if(quotationType == LineType::L1M){
//        YTXTimeDate newMonth = (tradedate1.date().end_of_month() + days(1)).end_of_month() + days(1);
//        return  newMonth >= tradedate2.date();
//    }
//    return false;
//}


bool GameMatrix::saveGameMatrixDataToDB(vector<GameMatrixItem> ret)
{
    GameMatrixSQLite gameMatrixSQLite;
    gameMatrixSQLite.createKLineDataTableIfNotExit(GameMatrixSQLite::getTableName(kLineData));
    return gameMatrixSQLite.insertData(GameMatrixSQLite::getTableName(kLineData), ret);
}

vector<GameMatrixItem> GameMatrix::getGameMatrixDataFromDB(YTXTime time, const unsigned int limit)
{
    vector<GameMatrixItem> ret;
    GameMatrixSQLite gameMatrixSQLite;
    
    gameMatrixSQLite.getDataFromDB(gameMatrixSQLite.getTableName(kLineData), time ,&ret, limit);
    
    return ret;
}

void GameMatrix::clearIfParentDataChanged(const Quotation * data)
{
    if (!isSameParentData(data)) {
        cleanData();
        requesLock = false;
    }
}

void GameMatrix::cleanData()
{
        gameMatrixData.clear();
        indexData.clear();
        lineData.clear();
}

int GameMatrix::getDataLength() const
{
    return (int)gameMatrixData.size();
}